using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Threading;

namespace OpenWaves.Web.ImageTransformations
{
    public class CompositeVirtualFileProvider : IVirtualFileProvider, IDisposable
    {
        private readonly IList<ProviderRegistration> registeredProviders;
        private readonly ReaderWriterLockSlim syncRoot = new ReaderWriterLockSlim();

        public CompositeVirtualFileProvider(IEnumerable<IVirtualFileProvider> providers)
        {
            Contract.Requires<ArgumentNullException>(providers != null);

            this.registeredProviders = providers.Select(p => new ProviderRegistration(p, url => true)).ToList();
        }

        #region IVirtualPathProvider Members

        public IVirtualFile GetFile(Url fileUrl)
        {
            this.syncRoot.EnterReadLock();
            try
            {
                foreach (var providerRegistration in this.registeredProviders)
                {
                    IVirtualFile file;
                    if (providerRegistration.TryGetFileInfo(fileUrl, out file))
                    {
                        return file;
                    }
                }
                return null;
            }
            finally
            {
                this.syncRoot.ExitReadLock();
            }
        }

        #endregion

        public CompositeVirtualFileProvider Add(IVirtualFileProvider provider)
        {
            Contract.Requires<ArgumentNullException>(provider != null);
            Contract.Ensures(Contract.Result<CompositeVirtualFileProvider>() != null);

            return this.Add(provider, url => true);
        }

        public CompositeVirtualFileProvider Add(IVirtualFileProvider provider, Predicate<Url> predicate)
        {
            Contract.Requires<ArgumentNullException>(provider != null);
            Contract.Requires<ArgumentNullException>(predicate != null);
            Contract.Ensures(Contract.Result<CompositeVirtualFileProvider>() != null);

            this.syncRoot.EnterWriteLock();
            try
            {
                this.registeredProviders.Add(new ProviderRegistration(provider, predicate));
                return this;
            }
            finally
            {
                this.syncRoot.ExitWriteLock();
            }
        }

        #region Nested type: ProviderRegistration

        private class ProviderRegistration
        {
            private readonly Predicate<Url> predicate;
            private readonly IVirtualFileProvider provider;

            public ProviderRegistration(IVirtualFileProvider provider, Predicate<Url> predicate)
            {
                this.provider = provider;
                this.predicate = predicate;
            }

            public bool TryGetFileInfo(Url fileUrl, out IVirtualFile virtualFile)
            {
                if (this.predicate(fileUrl) == false)
                {
                    virtualFile = null;
                    return false;
                }

                virtualFile = this.provider.GetFile(fileUrl);
                return virtualFile != null;
            }
        }

        #endregion

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.syncRoot.Dispose();
            }
        }
    }

}